﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ABB.Common.DataModel;
using System.Globalization;

namespace ABB.Common.BusinessLogic.Reporting
{
    /// <summary>
    /// <see cref="IComparer<ProjectPlanningDataSet.LotRow"/> für den Suchalgorithmus
    /// der Reports. Ermöglicht das Sortieren einer Liste nach den übergebenen Suchkriterien.
    /// </summary>
    internal class ReportSortCriteriaComparer : IComparer<ProjectPlanningDataSet.LotRow>
    {
        private readonly IReportDataLoader m_DataLoader;
        private readonly ProjectPlanningDataSet.UserRow m_CurrentUser;
        private readonly IEnumerable<ProjectPlanningDataSet.ReportSortCriterionRow> m_SortCriteria;

        /// <summary>
        /// Gibt den Typ an, wie die Daten zu vergleichen sind.
        /// </summary>
        private enum CompareType
        {
            StringCompare,
            NumericCompare,
            DateTimeCompare
        }

        public ReportSortCriteriaComparer(IReportDataLoader dataLoader,
            ProjectPlanningDataSet.UserRow currentUser,
            IEnumerable<ProjectPlanningDataSet.ReportSortCriterionRow> sortCriteria)
        {
            if (dataLoader == null) throw new ArgumentNullException("dataLoader");
            if (currentUser == null) throw new ArgumentNullException("currentUser");
            if (sortCriteria == null || sortCriteria.Count() == 0) throw new ArgumentException("sortCriteria");

            this.m_DataLoader = dataLoader;
            this.m_CurrentUser = currentUser;
            // Sortier kriterien direkt sortiert hinterlegen.
            this.m_SortCriteria = sortCriteria.OrderBy(c => c.SortIndex);
        }

        #region IComparer<LotRow> Member

        public int Compare(ProjectPlanningDataSet.LotRow x, ProjectPlanningDataSet.LotRow y)
        {
            // Die der Priorität nach sortierten Sortierkriterien durchgehen und solange durchlaufen,
            // bis eine Sortierung möglich ist.
            foreach (ProjectPlanningDataSet.ReportSortCriterionRow criterion in this.m_SortCriteria)
            {
                string xValue;
                string yValue;

                // Nur mit User laden bei der ChangeColumn, da ansonsten für
                // jedes Feld die ChangeLogs durchgegangen werden.
                if ((ReportColumnType)criterion.ReportColumnRow.Type == ReportColumnType.ChangeColumn)
                {
                    bool hasChanged;
                    xValue = this.m_DataLoader.GetValueForLotByReportColumn(x, criterion.ReportColumnRow,
                                                                            this.m_CurrentUser, out hasChanged);
                    yValue = this.m_DataLoader.GetValueForLotByReportColumn(y, criterion.ReportColumnRow,
                                                                            this.m_CurrentUser, out hasChanged);
                }
                else
                {
                    xValue = this.m_DataLoader.GetValueForLotByReportColumn(x, criterion.ReportColumnRow);
                    yValue = this.m_DataLoader.GetValueForLotByReportColumn(y, criterion.ReportColumnRow);
                }

                int ret = 0;

                switch (this.GetCompareTypeByReportColumn(criterion.ReportColumnRow))
                {
                    case CompareType.StringCompare:

                        ret = string.Compare(xValue, yValue);
                        break;

                    case CompareType.NumericCompare:

                        double xNumValue, yNumValue;
                        double.TryParse(xValue, out xNumValue);
                        double.TryParse(yValue, out yNumValue);

                        if (xNumValue < yNumValue)
                            ret = -1;
                        else if (xNumValue > yNumValue)
                            ret = 1;
                        else if (xNumValue == yNumValue)
                            ret = 0;

                        break;
                    case CompareType.DateTimeCompare:

                        DateTime xDateValue, yDateValue;
                        DateTime.TryParse(xValue, out xDateValue);
                        DateTime.TryParse(yValue, out yDateValue);

                        ret = DateTime.Compare(xDateValue, yDateValue);
                        break;
                }

                if (ret != 0)
                    return (ReportSortDirection)criterion.SortDirection == ReportSortDirection.Ascending
                                                    ? ret : ret * -1;
            }

            // Wenn kein Sortierkriterium einer Priorität ein anderse Ergebnis als Equal (0)
            // ergibt, dann geben wir immer 0 zurück.
            return 0;
        }

        #endregion

        #region Private Implementation

        private CompareType GetCompareTypeByReportColumn(
            ProjectPlanningDataSet.ReportColumnRow reportColumn)
        {
            ReportColumnType type = (ReportColumnType)reportColumn.Type;

            if (type == ReportColumnType.Label)
            {
                var label = this.m_DataLoader.GetLabelForReportColumn(reportColumn);
                var labelType = (LabelDataType)label.DataType;

                if (labelType == LabelDataType.DateTime) return CompareType.DateTimeCompare;
                if (labelType == LabelDataType.Float) return CompareType.NumericCompare;
                return CompareType.StringCompare;
            }
            else if (type == ReportColumnType.MilestoneInitialDate ||
                     type == ReportColumnType.MilestonePlanDate)
                return CompareType.DateTimeCompare;
            else
                return CompareType.StringCompare;
        }

        #endregion
    }
}
